package com.iteaj.iot.server.component;

import com.iteaj.iot.config.ConnectProperties;
import com.iteaj.iot.server.DeviceServerComponent;
import com.iteaj.iot.server.ServerMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.util.ReferenceCountUtil;

/**
 * 基于换行符的解码器组件
 * 支持 "\n" and "\r\n".
 * @see LineBasedFrameDecoder
 * @see DeviceServerComponent
 */
public abstract class LineBasedFrameDecoderComponent<M extends ServerMessage> extends SocketDecoderComponent<M> {

    private int maxLength;
    private boolean failFast;
    private boolean stripDelimiter;

    public LineBasedFrameDecoderComponent(ConnectProperties connectProperties, int maxLength) {
        this(connectProperties, maxLength, true, false);
    }

    public LineBasedFrameDecoderComponent(ConnectProperties connectProperties, int maxLength, boolean stripDelimiter, boolean failFast) {
        super(connectProperties);
        this.failFast = failFast;
        this.maxLength = maxLength;
        this.stripDelimiter = stripDelimiter;
    }

    @Override
    public ChannelInboundHandlerAdapter getMessageDecoder() {
        return new LineBasedFrameDecoderWrapper(this.maxLength, this.stripDelimiter, this.failFast);
    }

    protected class LineBasedFrameDecoderWrapper extends LineBasedFrameDecoder {

        public LineBasedFrameDecoderWrapper(int maxLength, boolean stripDelimiter, boolean failFast) {
            super(maxLength, stripDelimiter, failFast);
        }

        @Override
        protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
            final Object decode = super.decode(ctx, buffer);
            if(decode instanceof ByteBuf) {
                try {
                    final M proxy = LineBasedFrameDecoderComponent.this.proxy(ctx, (ByteBuf) decode);
                    return proxy != null ? proxy : decode;
                } finally {
                    ReferenceCountUtil.release(decode);
                }
            }

            return decode;
        }
    }
}
